#include "server.h"

Server::Server()
{
	if (SDLNet_ResolveHost(&this->ip, NULL, config.port()) < 0) {
		fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}
	
	if (!(this->socket = SDLNet_TCP_Open(&this->ip))) {
		fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	if(this->map.load(config.map_name().c_str())) this->map.blank(256,128,256);
	
	for (int i = this->config.max_players()-1; i >= 0; i--) {
		this->freeIDs.push(i);
	}
	
	this->socketSet = SDLNet_AllocSocketSet(this->config.max_players());
	if (!this->socketSet) {
		fprintf(stderr,"Fatal Error, closing. SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
		exit(1); //most of the time this is a major error, but do what you want.
	}
	
	this->heartbeat_ticks = -45000; // 0 makes it heartbeat at server load
	this->map_save = true;
	this->map_save_ticks = SDL_GetTicks();
	this->map_save_name = "";
	
	this->salt = 13801293;
	
	this->spleef = new Spleef;
	
	this->logFile.open("log.txt");
	if (!this->logFile.is_open()) printf("Error opening log file. Logging is disabled.\n");
}

Server::~Server()
{
	delete this->spleef;
	SDLNet_FreeSocketSet(this->socketSet);
	this->socketSet = NULL;
	this->logFile.close();
}

// Handles communication between clients
int Server::main_loop()
{
	while (1) {
		int time = SDL_GetTicks();
		
		this->spleef->update(this);
		
		// Add blocks from block_queue
		if (!this->block_queue.empty()) {
			unsigned int a = 0;
			int cp = this->clientList.size();
			int u = this->config.upload();
			a = (u/10 - (cp * (cp-1) * 12)) / 8;
			if (a > 25) a = 25;
			if (a <= 0) a = 25 / cp;
			if (cp == 0) a = this->block_queue.size();
			for ( ; a > 0 && !this->block_queue.empty(); a--) {
				block b = this->block_queue.front();
				send_block_all(b.x,b.y,b.z,b.type);
				this->block_queue.pop_front();
			}
		}
		
		if (SDLNet_CheckSockets(this->socketSet, 100) > 0) {
			char buffer[1024];
			for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
				if (i->second.socket_ready() != 0) {
					int ret = SDLNet_TCP_Recv(i->second.getSocket(), buffer, 1024);
					if (ret <= 0) {
						handle_disconnect(i->first);
						break;
					}
					handle_incoming_data(buffer, i->first, ret);					
				}
				
				handle_movement(i->first);
				i->second.set_moved(false);

				int ret = i->second.send_packet();
				if (ret) {	
					handle_disconnect(i->first);
					break;
				}
			}
		}
		int ticks = SDL_GetTicks();
		if (FPS_TIME > 0 && (ticks - time) <= FPS_TIME) {
			SDL_Delay(FPS_TIME - (ticks - time));
		}
	}
	return 0;
}

// Handles things like map saving and the heartbeat, and new connections. 
int Server::secondary_loop()
{
	TCPsocket tempSocket;
	while(1) {		
		if (tempSocket = SDLNet_TCP_Accept(this->socket)) {
			int id = this->freeIDs.top();
			this->freeIDs.pop();
			int ret = this->connect_client(id, tempSocket);
			if (ret == 1) this->freeIDs.push(id);
		} 
		if (SDL_GetTicks() - this->heartbeat_ticks > 45000) {
			IPaddress tempIP;
	
			if (SDLNet_ResolveHost(&tempIP, "www.minecraft.net", 80) < 0) {
				char temp[100];
				sprintf(temp, "Failed to resolve host \"www.minecraft.net\". \n%s", SDLNet_GetError());
				this->log(temp);
			}
			
			if (!(this->heartbeat_socket = SDLNet_TCP_Open(&tempIP))) {
				this->log("Failed to open heartbeat socket.\n");
			} else {	
				char temp[1024];
				char temp2[1024];
				sprintf(temp, "port=%d&users=%d&max=%d&name=%s&public=%s&version=7&salt=%d", 
					this->config.port(), 
					this->clientList.size(), 
					this->config.max_players(),
					this->config.name().c_str(), 
					this->config.is_public().c_str(), 
					this->salt
				);
				sprintf(temp2, "POST /heartbeat.jsp HTTP/1.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:%d\r\nContent-Language: en-US\r\nCache-Control: no-cache\r\nPragma:no-cache\r\nUser-Agent: Java/1.6.0_16\x0D\x0AHost:www.minecraft.net\r\nAccept: text/html, image/gif, image/jpeg, *; q=.2,*/*; q=.2\r\nConnection: keep-alive\r\n\r\n%s", strlen(temp), temp);
				unsigned int ret = SDLNet_TCP_Send(this->heartbeat_socket, temp2, strlen(temp2));
				if(ret < strlen(temp)) fprintf(stderr,"Failed to send heartbeat.\n");
				SDLNet_TCP_Recv(this->heartbeat_socket, temp, 1024);
				static bool first = true;
				if(first) {
				}
			}
			this->heartbeat_ticks = SDL_GetTicks();
		}
		if (this->map_save || SDL_GetTicks() - this->map_save_ticks > 60000) {
			char temp[100];
			if (this->map_save_name == "") {
				sprintf(temp,"Saving map to %s\n", this->config.map_name().c_str());
				this->log(temp);
				this->map.save(this->config.map_name().c_str());
				this->map_save_ticks = SDL_GetTicks();
			} else { 
				sprintf(temp,"Saving map to %s\n",this->map_save_name.c_str());
				this->log(temp);
				this->map.save(this->map_save_name.c_str());
				this->map_save_name = "";
			}
			this->map_save = false;
		}
		SDL_Delay(100);
	}
	return 0;
}

void Server::handle_incoming_data(char* buffer, int id, char len)
{
	int offset = 0;
	while(offset < len) {
		switch(buffer[offset]) {
			case 5: {
				short x = SDLNet_Read16(&buffer[offset+1]);
				short y = SDLNet_Read16(&buffer[offset+3]);
				short z = SDLNet_Read16(&buffer[offset+5]);
				bool create = buffer[offset+7];
				char type = buffer[offset+8];
				offset += 9;
				this->clientList[id].set_last_block(-1, x,y,z,create ? type : 0);
				if (this->clientList[id].isFrozen()) {
					send_block(x,y,z, this->map(x,y,z), id);
				} else {
					if (x == y && y == z && z == 65) send_block(x,y,z, this->map(x,y,z), id);
					else {
						for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
							send_block(x,y,z, create ? type : 0, i->first);
							this->map(x,y,z, create ? type : 0);
						}
					}
				}
				break;
			}
			case 8: {
				if(this->clientList[id].isMoved()) {
					offset+=10;
					break;
				}
				int x, y, z, rx, ry;
				x = SDLNet_Read16(&buffer[offset+2]);
				y = SDLNet_Read16(&buffer[offset+4]);
				z = SDLNet_Read16(&buffer[offset+6]);
				rx = buffer[offset+8];
				ry = buffer[offset+9];
				this->clientList[id].set_pos(x,y,z,rx,ry);
				offset += 10;
				break;
			}
			case 0x0D: {
				for(int a = 62; a > 0; a--) {
					if (buffer[offset+2+a] != 20) { 
						buffer[offset+2+a+1] = 0;
						break;
					}
				}
				if (buffer[offset+2] == '/') {
					if (parse_command(id, &buffer[offset+2])) {
						offset +=66;
						break;
					}
				}
				if (!this->clientList[id].mute) {
					char temp[64];
					memset(temp, 0, sizeof(temp));
					strcpy(temp, this->clientList[id].getName().c_str());
					strncat(temp, ": ", 2);
					strncat(temp, &buffer[offset+2], 64);	
					this->log(temp);
					send_chat_all(id, temp);					
				}
				offset += 66;
				break;
			}
			default: 
				fprintf(stderr,"Bad data @ %i, aborting.", offset);
				for (int a = 0; a < len; a++) fprintf(stderr," %i", static_cast<unsigned char>(buffer[a]));
				offset += len;
		}
	}
}

int Server::parse_command(int id, char* msg)
{	
	char arg[5][64]; 
	int p = 1;
	int l = 0;
	for (int a = 0; a < 5 && p+l < (int)strlen(msg); a++) {
		memset(arg[a], 0, 64);
		l = strcspn(&msg[p], " ");
		memcpy(arg[a], &msg[p], l);
		p = p+l+1;
	}
	
	if (strcmp(arg[0], "kick") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
				if (strcmp(i->second.getName().c_str(),arg[1]) == 0) {
					send_kick("You were kicked.", i->first);
					return 1;
				}
			}
			send_chat(255, "Player doesn't exist.", id);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	if (strcmp(arg[0], "tp") == 0) {
		for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
			if (strcmp(i->second.getName().c_str(),arg[1]) == 0) {
				int x, y, z, rx, ry;
				i->second.get_pos(&x,&y,&z,&rx,&ry);
				this->clientList[id].move(x,y,z,rx,ry);
				return 1;
			}
		}
		send_chat(255, "Player doesn't exist.", id);
		return 1;
	}
	if (strcmp(arg[0], "save") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			if (strcmp("", arg[1]) != 0) {
				map_save_name = "backup\\";
				map_save_name += arg[1];
			}
			send_chat(255, "Map was saved.", id);
			this->map_save = true;
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	if (strcmp(arg[0], "pos") == 0) {
		char temp[64]; 
		memset(temp, 0, sizeof(temp));
		int x,y,z,rx,ry;
		this->clientList[id].get_pos(&x,&y,&z,&rx,&ry);
		sprintf(temp, "You are at x:%i y:%i z:%i.", x/32, y/32, z/32);
		send_chat(255, temp, id);
		return 1;
	}
	if (strcmp(arg[0], "help") == 0) {
		send_chat(255, "Note: All arg[0]s are case-sensitive", id);
		send_chat(255, "/pos /tp player", id);
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			send_chat(255, "/mute /unmute /op /deop /draw /save", id);
		}
		return 1;
	}

	if (strcmp(arg[0], "spleef") == 0) {
		if (strcmp(arg[1], "start") == 0) {
			this->spleef->set_phase(0);
			this->send_chat_all(255, "A spleef game is starting. To join type /spleef join");
			return 1;
		} else if (strcmp(arg[1], "ready") == 0) {
			this->spleef->set_phase(1);
			return 1;
		} else if (strcmp(arg[1], "join") == 0) {
			if (this->spleef->can_join()) {
				this->spleef->add_player(id);
				char tempChar[64];
				memset(tempChar, 0, sizeof(tempChar));
				sprintf(tempChar, "%s has joined!", this->clientList[id].getName().c_str());						
				this->send_chat_all(255, tempChar);
				return 1;
			} else {
				send_chat(255, "A spleef game currently isn't going.", id);
				return 1;
			}
		}
	}
	
	/*
	if (strcmp(arg[0], "freeze") == 0) {
		if (config.admins.count(this->clientList[id].name) == 1) {
			for(int a = 0; a < config.max_players; a++) {
				if (strcmp(this->clientList[a].name,arg[1]) == 0 && this->clientList[a].active) {
					Client* t = &this->clientList[a];
					if (t->frozen) {
						t->frozen = false;
						send_chat(255, "You have been unfrozen.", a);
					} else {
						t->frozen = true;
						send_chat(255, "You have been frozen.", a);
					}
					return 1;
				}
			}
		} else {
			send_chat(255, "Admin only arg[0].", id);
			return 1;
		}
		send_chat(255, "Player doesn't exist.", id);
		return 1;
	}
	*/
	
	if (strcmp(arg[0], "op") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			this->config.add_admin(arg[1]);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "deop") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			this->config.del_admin(arg[1]);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "ban") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			this->config.add_ban(arg[1]);
			for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
				if (strcmp(i->second.getName().c_str(),arg[1]) == 0) {
					send_kick("You were banned.", i->first);
					return 1;
				}
			}
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "unban") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			this->config.del_ban(arg[1]);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "mute") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
				if (strcmp(i->second.getName().c_str(),arg[1]) == 0) {
					i->second.mute = true;
					send_chat(255, "You were muted.", i->first);
					return 1;
				}
			}
			send_chat(255, "Player doesn't exist.", id);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "unmute") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
				if (strcmp(i->second.getName().c_str(),arg[1]) == 0) {
					i->second.mute = false;
					send_chat(255, "You were unmuted.", i->first);
					return 1;
				}
			}
			send_chat(255, "Player doesn't exist.", id);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}
	}
	
	if (strcmp(arg[0], "draw") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			int x,y,z,type, x2,y2,z2;
			this->clientList[id].get_last_block(1, &x2, &y2, &z2, NULL);
			this->clientList[id].get_last_block(0, &x, &y, &z, &type);
			this->set_box(x,y,z,x2,y2,z2, type);
			send_chat(255, "Drawing...", id);
			return 1;
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}		
	}
	
	if (strcmp(arg[0], "prefab") == 0) {
		if (config.is_admin(this->clientList[id].getName()) == 1) {
			if (strcmp(arg[1], "save") == 0) {
				int x,y,z,x2,y2,z2;
				this->clientList[id].get_last_block(1, &x2, &y2, &z2, NULL);
				this->clientList[id].get_last_block(0, &x, &y, &z, NULL);
				int w,h,d;
				if ( x > x2) { int temp = x2; x2 = x; x = temp;} 
				if ( y > y2) { int temp = y2; y2 = y; y = temp;} 
				if ( z > z2) { int temp = z2; z2 = z; z = temp;}
				w = x2 - x + 1; h = y2 - y + 1; d = z2 - z + 1;
				char* temp = new char[w*h*d];
				for (int a = x; a <= x2; a++) {
					for (int b = y; b <= y2; b++) {
						for (int c = z; c <= z2; c++) {
							temp[(a-x) + ((b-y) * d + (c-z)) * w] = this->map(a,b,c);
						}
					}
				}
				char name[64] = "prefab\\";
				strcat(name, arg[2]);
				gzFile gfile = gzopen(name, "wb");
				if (gfile == NULL) {
					send_chat(255, "Error opening file.", id);
					delete [] temp;
					return 1;
				}
				else {
					gzwrite(gfile, &w, 4);
					gzwrite(gfile, &h, 4);
					gzwrite(gfile, &d, 4);
					gzwrite(gfile, temp, w*h*d);
					gzclose(gfile);
				}
				delete [] temp;
				send_chat(255, "Successly saved prefab.", id);
				return 1;
			} else if (strcmp(arg[1], "load") == 0) {
				int x,y,z,d,h,w;
				this->clientList[id].get_last_block(0, &x, &y, &z, NULL);
				char* temp;
				char name[64] = "prefab\\";
				strcat(name, arg[2]);
				gzFile gfile = gzopen(name, "rb");
				if (gfile == NULL) {
					send_chat(255, "Error opening file.", id);
					delete [] temp;
					return 1;
				}
				else {
					gzread(gfile, &w, 4);
					gzread(gfile, &h, 4);
					gzread(gfile, &d, 4);
					temp = new char[w*h*d];
					gzread(gfile, temp, w*h*d);
					gzclose(gfile);
				}
				int x2 = x-(w/2)+w; int z2 = z-(d/2)+d;
				x = x-(w/2); z = z-(d/2);
				for (int a = x; a < x2; a++) {
					for (int b = y; b < y + h; b++) {
						for (int c = z; c < z2; c++) {
							this->set_block(a,b,c,temp[(a-x) + ((b-y) * d + (c-z)) * w]);
						}
					}
				}
				delete [] temp;
				send_chat(255, "Successly loaded prefab.", id);
				return 1;
			}
		} else {
			send_chat(255, "Admin only arg[0].",id);
			return 1;
		}	
	}
	
	send_chat(255, "Not a command. Try /help", id);
	return 1;
}

int Server::handle_movement(int id) 
{
	int x,y,z,rx,ry;
	int ox,oy,oz,orx,ory;
	
	this->clientList[id].get_pos(&x,&y,&z,&rx,&ry);
	this->clientList[id].get_pos_old(&ox,&oy,&oz,&orx,&ory);

	if (this->clientList[id].isFrozen()) {
		send_player_update(255, ox, oy, oz, orx, ory, id);
	} else {
		bool br, bp;
		br = (rx != orx) || (ry != ory);
		bp = (x != ox) || (y != oy) || (z != oz);

		if (bp || br) {
			short dx = x - ox;
			short dy = y - oy;
			short dz = z - oz;
			if (br && !bp ) { // Rotation only
				for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) 
					if(i->first != id)
						send_player_rot(id, rx, ry, i->first);
			} else if (!br && bp &&
					   ((-127 <= dx && dx <= 127) && (-127 <= dy && dy <= 127) && (-127 <= dz && dz <= 127))) { // Movement only
				for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) 
					if(i->first != id)
						send_player_movement(id, char(dx), char(dy),char(dz), i->first);
			} else if (bp && br &&
					   ((-127 <= dx && dx <= 127) && (-127 <= dy && dy <= 127) && (-127 <= dz && dz <= 127))) {  // Movement and rotation
				for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) 
					if(i->first != id)
						send_player_update_small(id, char(dx), char(dy),char(dz), rx, ry, i->first);
			} else if (!this->clientList[id].isMoved()){ // Position and rotation
				for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) 
					if(i->first != id) 
						send_player_update(id, x, y, z, rx, ry, i->first);
			} else {
				send_player_update_all(id, x, y, z, rx, ry);
			}
			this->clientList[id].set_pos_old(x,y,z,rx,ry);
		}
	}
	return 0;
}

int Server::connect_client(int id, TCPsocket socket)
{	
	char buffer[1024];
	int bytes = 0;
	bytes = SDLNet_TCP_Recv(socket, buffer, 1024);
	if (bytes == 131) {

		std::string cname;
		std::string csname;
		
		for (int a = 0; buffer[a+2] != ' '; a++) // Set player name
			cname += buffer[a+2];
		csname.copy(&buffer[2], 64, 0);
		
		if (this->config.is_banned(cname)) {
			memset(buffer, 20, 65);
			buffer[0] = 0x0E;
			strcpy(&buffer[1], "You are banned.");
			SDLNet_TCP_Send(socket, buffer, 65);
			SDLNet_TCP_Close(socket);
			return 1;
		}
		
		// Sets spawn position of client
		int x = this->map.WIDTH()*16+32; 
		int y = this->map.HEIGHT()*16+32; 
		int z = this->map.DEPTH()*16+32;
		int rx = 0;
		int ry = 0;		
		
		IPaddress* clientIP;
		clientIP = SDLNet_TCP_GetPeerAddress(socket);
		int ipaddr = SDLNet_Read32(&clientIP->host);
		char temp[64];
		char tempStr[100];
		sprintf(tempStr,"\"%s\" is connecting from IP %i.%i.%i.%i. \n", cname.c_str(), ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff);
		this->log(tempStr);
		sprintf(temp, "%s is connecting.", cname.c_str());
		this->send_chat_all(255, temp);

		std::string name = config.name();
		std::string motd = config.motd();
		std::string::iterator iname, imotd;
		iname = name.begin();
		imotd = motd.begin();
		for (int a = 0; a < 64; a++) {
			buffer[a+2] = (iname < name.end()) ? *iname : ' ';
			buffer[a+66] = (imotd < motd.end()) ? *imotd : ' ';
			iname++; imotd++;
		}
		bool admin = false;
		if(config.is_admin(cname)) {
			buffer[130] = 0x64;
			admin = true;
		}
		SDLNet_TCP_Send(socket, buffer, 131);
		
		// Create the client object		
		//*
		int sourceSize = map.WIDTH()*map.DEPTH()*map.HEIGHT();
		char* mapSnapShot = new char[sourceSize+4];	
		SDLNet_Write32(int(sourceSize),mapSnapShot);
		if(map.dump(&mapSnapShot[4])) {
			fprintf(stderr,"Fatal Error: Unable to dump map\n.");
			exit(1);
		}
		
		gzFile tempFile = gzopen("temp", "wb");
		gzwrite(tempFile, mapSnapShot, sourceSize+4);
		gzclose(tempFile);
		std::fstream file("temp");
		file.seekg(0, std::ios::end);
		int mapBuffSize = file.tellg();
		char* mapBuffer = new char[mapBuffSize];
		file.seekg(0, std::ios::beg);
		file.read(mapBuffer, mapBuffSize);
		file.close();
		remove("temp");

		buffer[0] = 02;
		SDLNet_TCP_Send(socket, buffer, 1);
		
		for ( int a = 0; ; ) {
			if (a + 1024 < mapBuffSize) {
				buffer[0] = 03;
				SDLNet_Write16(1024, &buffer[1]);
				SDLNet_TCP_Send(socket, buffer, 3);
				SDLNet_TCP_Send(socket, &mapBuffer[a], 1024);
				buffer[0] = char(a / (mapBuffSize-4) * 100);
				SDLNet_TCP_Send(socket, buffer, 1);
				a += 1024;
			} else if (a + 1024 > mapBuffSize) {
				int l = mapBuffSize - a;
				buffer[0] = 03;
				SDLNet_Write16(l, &buffer[1]);
				SDLNet_TCP_Send(socket, buffer, 3);
				SDLNet_TCP_Send(socket, &mapBuffer[a], l);
				int size = 1024-l;
				char* temp = new char[size];
				memset(temp, 0, size);
				SDLNet_TCP_Send(socket, temp, size);
				buffer[0] = 100;
				SDLNet_TCP_Send(socket, buffer, 1);
				delete [] temp;
				break;
			}
		}
		
		delete [] mapSnapShot;
		delete [] mapBuffer;

		buffer[0] = 04;
		SDLNet_Write16(map.WIDTH(),&buffer[1]);
		SDLNet_Write16(map.HEIGHT(),&buffer[3]);
		SDLNet_Write16(map.DEPTH(),&buffer[5]);
		SDLNet_TCP_Send(socket, buffer, 7);
		//*
		this->clientList.insert(std::pair<int, Client>(id,Client(id, cname, csname, x,y,z,rx,ry, socket, admin)));
		// TODO clean this code up
		for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
			send_spawn( (*i).first == id ? 255 : id, cname, x, y, z, 0, 0, (*i).first);
			int x2,y2,z2,rx2,ry2;
			i->second.get_pos(&x2,&y2,&z2,&rx2,&ry2);
			if (i->first != id) send_spawn(i->first, i->second.getName(), x2, y2, z2, rx2, ry2, id);
		}
		sprintf(tempStr,"%s has connected!\n", cname.c_str());
		this->log(tempStr);
		//* TODO
		memset(temp, 0, sizeof(temp));
		strcpy(temp, cname.c_str());
		strncat(temp, " has joined the server.", 64);
		send_chat_all(255, temp);
		memset(temp, 0, sizeof(temp));
		strcpy(temp, this->config.motd().c_str());
		send_chat(255, temp, id);
		//*/

		SDLNet_TCP_AddSocket(this->socketSet, socket);
	}
	return 0;
}

void Server::handle_disconnect(int id)
{
	send_player_delete_all(id);
	send_kick("hi", id);
	char temp[64];
	sprintf(temp, "%s has disconnected.\n", this->clientList[id].getName().c_str());
	send_chat_all(255, temp);
	SDLNet_TCP_DelSocket(this->socketSet, this->clientList[id].getSocket());
	SDLNet_TCP_Close(this->clientList[id].getSocket());
	this->clientList.erase(id);
	this->freeIDs.push(id);
	this->log(temp);
}

int Server::send_block(short x, short y, short z, char type, int i)
{ 
	char buffer[8];
	buffer[0] = 6;
	SDLNet_Write16(x, &buffer[1]);
	SDLNet_Write16(y, &buffer[3]);
	SDLNet_Write16(z, &buffer[5]);
	buffer[7] = type;
	this->clientList[i].append_packet(buffer, 8);
	return 0;
	
}

void Server::send_block_all(short x, short y, short z, char type, int exclude)
{
	for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) if(i->first != exclude) send_block(x,y,z,type,i->first);
}

int Server::send_spawn(int id, std::string name, short x, short y, short z, char rx, char ry, int i)
{ 
	char buffer[74];
	buffer[0] = 07;
	buffer[1] = id;
	memset(&buffer[2], 0x20, 64);
	strcpy(&buffer[2], name.c_str());
	SDLNet_Write16(x,&buffer[66]);
	SDLNet_Write16(y,&buffer[68]);
	SDLNet_Write16(z,&buffer[70]);
	buffer[72] = rx;
	buffer[73] = ry;
	this->clientList[i].append_packet(buffer, 74);
	return 0;
}

int Server::send_player_update(int id, short x, short y, short z, char rx, char ry, int i)
{ 	
	char buffer[10];
	buffer[0] = 8;
	buffer[1] = id;
	SDLNet_Write16(x, &buffer[2]);
	SDLNet_Write16(y, &buffer[4]);
	SDLNet_Write16(z, &buffer[6]);
	buffer[8] = rx; 
	buffer[9] = ry;
	this->clientList[i].append_packet(buffer, 10);
	return 0;
} 

void Server::send_player_update_all(int id, short x, short y, short z, char rx, char ry)
{
	for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) send_player_update(id == i->first ? 255 : id, x, y, z, rx, ry, i->first);
}

int Server::send_player_update_small(int id, char x, char y, char z, char rx, char ry, int i)
{	
	char buffer[7];
	buffer[0] = 9;
	buffer[1] = id;
	buffer[2] = x;
	buffer[3] = y;
	buffer[4] = z;
	buffer[5] = rx; 
	buffer[6] = ry;
	this->clientList[i].append_packet(buffer, 7);
	return 0;
}

int Server::send_player_movement(int id, char x, char y, char z, int i)
{
	char buffer[5];
	buffer[0] = 10;
	buffer[1] = id;
	buffer[2] = x;
	buffer[3] = y;
	buffer[4] = z;
	this->clientList[i].append_packet(buffer, 5);
	return 0;
}

int Server::send_player_rot(int id, char rx, char ry, int i)
{
	char buffer[4];
	buffer[0] = 11;
	buffer[1] = id;
	buffer[2] = rx;
	buffer[3] = ry;
	this->clientList[i].append_packet(buffer, 4);
	return 0;
}

int Server::send_player_delete(int id, int i)
{ 
	char buffer[2];
	buffer[0] = 0x0C;
	buffer[1] = id;
	this->clientList[i].append_packet(buffer, 2);
	return 0;
}

void Server::send_player_delete_all(int id)
{
	for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) send_player_delete(id, i->first);
}

int Server::send_chat(int id, char* msg, int i)
{
	char buffer[66];
	buffer[0] = 0x0D;
	buffer[1] = id;
	memset(&buffer[2], 0x14, 64);
	strcpy(&buffer[2], msg);
	this->clientList[i].append_packet(buffer, 66);
	return 0;
}

void Server::send_chat_all(int id, char* msg)
{
	for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) send_chat(id, msg, i->first);
}

int Server::send_kick(char* reason, int i)
{ 
	char buffer[65];
	buffer[0] = 0x0E;
	strncpy(&buffer[1], reason, 64);
	SDLNet_TCP_Send(this->clientList[i].getSocket(), buffer, 65);
	return 0;

}

void Server::set_box(int x,int y,int z, int x2, int y2, int z2, char type, int thickness, int type2)
{
	if ( x > x2) { int temp = x2; x2 = x; x = temp;} 
	if ( y > y2) { int temp = y2; y2 = y; y = temp;} 
	if ( z > z2) { int temp = z2; z2 = z; z = temp;} 
	for (int a = x; a <= x2; a++) {
		for (int b = y; b <= y2; b++) {
			for (int c = z; c <= z2; c++) {
				if (thickness == 0)	set_block(a,b,c,type);
				else {
					if (a < x+thickness || a >= x2-thickness || b < y+thickness || b >= y2-thickness || c < z+thickness || c >= z2-thickness)
						set_block(a,b,c,type);
					else if (type2 != -1)
						set_block(a,b,c,type2);
				}
			}		
		}
	}
}

void Server::set_block(int x, int y, int z, char type)
{
	if (type > 0x30 || type < 0) {
		char temp[100];
		sprintf(temp, "Warning: Attempting to send block invalid block type %i.\n", type);
		this->log(temp);
	} else if (x < 0 || y < 0 || z < 0 || x > this->map.WIDTH() || y > this->map.HEIGHT() || z > this->map.DEPTH()) {
		char temp[100];
		sprintf(temp, "Warning: Attepting to create block at invalid coords x:%i y:%i z:%i\n", x,y,z);
		this->log(temp);
	} else if (this->map(x,y,z)  != type) {
		block b; 
		b.x = x; 
		b.y = y; 
		b.z = z; 
		b.type = type;
		this->block_queue.push_back(b);
		this->map(x,y,z,type);
	}

}

void Server::find_players(int x, int y, int z, int x2, int y2, int z2, std::set<char>* ids)
{
	x*=32;y=(y*32)+63;z*=32;x2=(x+1)*32;y2*=32;z2=(z+1)*32;
	for (std::map<int, Client>::iterator i = this->clientList.begin(); i != this->clientList.end(); i++) {
		bool a, b, c;
		int cx, cy, cz;
		i->second.get_pos(&cx, &cy, &cz, NULL, NULL);
		a = cx > x && cx < x2;
		b = cy > y && cy < y2;
		c = cz > z && cz < z2;
		if (a && b && c) {
			ids->insert(i->first);
		}
	}
}

int Server::is_connected(int id)
{
	return this->clientList.count(id);
}

void Server::move_player(int x, int y, int z, int rx, int ry, int id)
{
	this->clientList[id].move(x,y,z,rx,ry);
}

std::string Server::get_player_name(int id)
{
	return this->clientList[id].getName();
}

void Server::log(char* name)
{
	printf("%s %s", get_time(), name);
	if (name[strlen(name)-1] != '\n') printf("\n");
	if(this->logFile.is_open()) { 
		this->logFile << get_time() << " " << name;
		if (name[strlen(name)-1] != '\n') this->logFile << std::endl;
		this->logFile.flush();
	}
}

char* Server::get_time()
{
	time_t rawtime;
	struct tm* t;
	
    time(&rawtime);
    t = localtime(&rawtime);
	sprintf(this->ltime, "[%02i:%02i:%02i] ",t->tm_hour, t->tm_min, t->tm_sec);
	return this->ltime;
}

int Server::main_loop_run(void* param)
{
	return ((Server*)param)->main_loop();
}

int Server::secondary_loop_run(void* param)
{
	return ((Server*)param)->secondary_loop();
}
